import urllib2, json, string, random, cookielib
import errors


DUMMY_USER_AGENT = 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'

cj = cookielib.LWPCookieJar()
opener = urllib2.build_opener(urllib2.HTTPRedirectHandler(), urllib2.HTTPCookieProcessor(cj))
urllib2.install_opener(opener)


def _encode(s, safe='.-_~', encoding='utf-8'):
    if type(s) == unicode:
        s = s.encode(encoding)
    return urllib2.quote(s, safe)

def encode(s, safe='.-_~', encoding='utf-8'):
    if type(s) is dict:
        s2 = []
        for k in s.keys():
            v = s[k]
            s2.append("%s=%s" % (_encode(k, safe, encoding), _encode(v, safe, encoding)))
        return s2
    else:
        return _encode(s, safe, encoding)
    

def randstr(length, chars=string.ascii_uppercase + string.ascii_lowercase + string.digits):
    return ''.join(random.choice(chars) for _ in range(length))

def makeheaders(referer=None):
    headers = {'User-agent':DUMMY_USER_AGENT}
    if referer is not None:
        headers['Referer'] = referer
    return headers

def openurl(url, data=None, referer=None, opener=None, timeout=30, start=None, end=None):
    try:
        headers = makeheaders(referer)
        if not (start is None and end is None):
            start = '' if start is None else str(start)
            end = '' if end is None else str(end)
            headers['Range'] = 'bytes=%s-%s' % (start, end)
        req = urllib2.Request(url, headers=headers)
        if opener is None:  
            return urllib2.urlopen(req, data, timeout=timeout)
        return opener.open(req, data, timeout=timeout)
    except urllib2.HTTPError, e:
        raise errors.check_status(e.code, e.read())
    except Exception, e:
        raise errors.KuaiPanNetError(str(e))

def readurl(url, data=None, referer=None, opener=None, timeout=30):
    try:
        f = openurl(url, data, referer, opener, timeout)
        s = f.getcode()
        return f.read(), s
    except urllib2.HTTPError,e:
        return e.read(), e.code
    except errors.KuaiPanError:
        raise
    except Exception, e:
        raise errors.KuaiPanNetError(str(e))

def loadjson(s):
    try:
        return json.loads(s)
    except Exception, e:
        raise errors.KuaiPanDataError(str(e))

from urllib import splittype, splithost, addinfourl
import socket, mimetools, mimetypes

class UploadFileRequest(urllib2.Request):
    CHUNK_SIZE = 65536
    _file_head_template = '--%s\r\n'\
                     'Content-Disposition: form-data; name="%s"; filename="%s";\r\n'\
                     'Content-Type: %s\r\n'\
                     'Content-Length: %s\r\n\r\n'

    _end_template = '\r\n--%s--\r\n\r\n'
                     
    _field_template = '--%s\r\n'\
                     'Content-Disposition: form-data; name="%s";\r\n\r\n'\
                     '%s\r\n'

    
    def __init__(self, url, files={}, callback=None, *args, **kwargs):
        urllib2.Request.__init__(self, url, *args, **kwargs)
        self.files = files
        self.boundary = mimetools.choose_boundary()
        self.callback = callback

    
    def split_data(self, data):
        t = type(data)
        r = {}
        if t is str or t is unicode:
            d = data.split('&')
            for i in d:
                v = i.split('=')
                if len(v) >=2:
                    r[v[0]] = v[1]
        elif t is dict:
            r = data
        elif t is list:
            for i in data:
                v = i.split('=')
                if len(v) >=2:
                    r[v[0]] = v[1]
        return r
    
    def str_data(self, data):
        t = type(data)
        r = ''
        if t is str or t is unicode:
            r = data
        elif t is dict:
            r = '&'.join(['%s=%s' % (k, v) for k,v in data.items()])
        elif t is list:
            r = '&'.join(data)
        return r
    
    def has_file(self):
        return len(self.files) > 0
    
    def add_file(self, field, f, size, filename='', mimetype=None):
        if mimetype is None:
            mimetype =  mimetypes.guess_type(filename)[0] or 'application/octet-stream'
        self.files[field] = (f, size, filename, mimetype)
        
    def get_file(self):
        return self.files
    
    def get_method(self):
        if self.has_data() or self.has_file():
            return "POST"
        else:
            return "GET"
        
    def get_content_type(self):
        if self.has_file():
            return 'multipart/form-data; boundary=%s' % self.boundary
        return 'application/x-www-form-urlencoded'
    
    def get_length(self):
        if self.has_file():
            length = 0
            data = self.split_data(self.get_data())
            for k, v in data.items():
                s = UploadFileRequest._field_template % (self.boundary, k, v)
                length += len(s)
            
            files = self.get_file()
            for k, v in files.items():
                s= UploadFileRequest._file_head_template % (self.boundary, k, v[2], v[3], v[1])
                length += len(s) + v[1];
                
            s = UploadFileRequest._end_template % self.boundary
            length += len(s)
            return length 
        elif self.has_data():
            data = self.str_data(self.get_data())
            return len(data)
        return 0
    
    def _do_send(self, conn, s):
        conn.send(s)
        self.sent += len(s)
        if self.callback is not None:
            self.callback(self.total, self.sent)
    
    def _send_file_data(self, conn, f):
        if hasattr(f, 'seek'):
            f.seek(0)
        while True:
            s = f.read(UploadFileRequest.CHUNK_SIZE)
            if not s:
                break
            self._do_send(conn, s)
            
    def send(self, conn):
        self.total = self.get_length()
        self.sent = 0
        
        if self.has_file():
            data = self.split_data(self.get_data())
            for k, v in data.items():
                s = UploadFileRequest._field_template % (self.boundary, k, v)
                self._do_send(conn, s)
            
            files = self.get_file()
            for k, v in files.items():
                s= UploadFileRequest._file_head_template % (self.boundary, k, v[2], v[3], v[1])
                self._do_send(conn, s)
                self._send_file_data(conn, v[0])
            s = UploadFileRequest._end_template % self.boundary
            self._do_send(conn, s)
        elif self.has_data():
            data = self.str_data(self.get_data())
            left = len(data)
            while left > 0:
                s = data[0 : UploadFileRequest.CHUNK_SIZE]
                data = data[UploadFileRequest.CHUNK_SIZE : ]
                self._do_send(conn, s)
                left= len(data)


def _upload_file_do_request(self, request):
    host = request.get_host()
    if not host:
        raise urllib2.URLError('no host given')

    if not request.has_header('Content-type'): 
        request.add_unredirected_header('Content-type', request.get_content_type())
    
    if not request.has_header('Content-length'):
            request.add_unredirected_header('Content-length', '%d' % request.get_length())
    
    sel_host = host
    if request.has_proxy():
        _, sel = splittype(request.get_selector())
        sel_host, _ = splithost(sel)

    if not request.has_header('Host'):
        request.add_unredirected_header('Host', sel_host)
    for name, value in self.parent.addheaders:
        name = name.capitalize()
        if not request.has_header(name):
            request.add_unredirected_header(name, value)

    return request

class HTTPUploadFileHandler(urllib2.HTTPHandler):

    http_request = _upload_file_do_request
   
    def do_open(self, http_class, req):
        host = req.get_host()
        if not host:
            raise urllib2.URLError('no host given')

        # will parse host:port
        h = http_class(host, timeout=req.timeout)
        h.set_debuglevel(self._debuglevel)

        headers = dict(req.unredirected_hdrs)
        headers.update(dict((k, v) for k, v in req.headers.items()
                            if k not in headers))
        headers["Connection"] = "close"

        if req._tunnel_host:
            tunnel_headers = {}
            proxy_auth_hdr = "Proxy-Authorization"
            if proxy_auth_hdr in headers:
                tunnel_headers[proxy_auth_hdr] = headers[proxy_auth_hdr]
                del headers[proxy_auth_hdr]
            h.set_tunnel(req._tunnel_host, headers=tunnel_headers)

        try:
            h.putrequest(req.get_method(), req.get_selector())
            
            for k, v in headers.items():
                if k != 'Host':
                    h.putheader(k, v)
            
            h.endheaders()
            req.send(h)
        except socket.error, err: 
            h.close()
            raise urllib2.URLError(err)
        else:
            try:
                r = h.getresponse(buffering=True)
            # buffering kw not supported
            except TypeError: 
                r = h.getresponse()

        # Pick apart the HTTPResponse object to get the addinfourl
        # object initialized properly.

        # Wrap the HTTPResponse object in socket's file object adapter
        # for Windows.  That adapter calls recv(), so delegate recv()
        # to read().  This weird wrapping allows the returned object to
        # have readline() and readlines() methods.

        # XXX It might be better to extract the read buffering code
        # out of socket._fileobject() and into a base class.

        r.recv = r.read
        fp = socket._fileobject(r, close=True)

        resp = addinfourl(fp, r.msg, req.get_full_url())
        resp.code = r.status
        resp.msg = r.reason
        return resp


def uploadfile(url, f, size, field='file', filename='', data=None, timeout=30, callback=None):
    opener = urllib2.build_opener(HTTPUploadFileHandler)
    req = UploadFileRequest(url, data=data, headers=makeheaders(), callback=callback)
    req.add_file(field, f, size, filename)
    try:
        f = opener.open(req, timeout=timeout)
        s = f.getcode()
        return f.read(), s
    except urllib2.HTTPError,e:
        return e.read(), e.code
    except Exception, e:
        raise errors.KuaiPanNetError(str(e))
    
